# -*- coding: utf-8 -*-
"""
Author: chen xi
Date: 2025/5/6 下午2:29
File: app_flow_graph.py
"""

import json
import os.path
from configparser import ConfigParser
import networkx as nx
from adjacency_list_excel_manager import AdjacencyListExcelManager
from logging_utils import Logger


class AppFlowGraph:
	"""
    表示应用程序页面的有向图，并提供与页面流相关的功能。
    包括从配置文件中加载数据、创建有向图、计算最短路径、评估页面信任分数等。
    """
	
	def __init__(self, config: ConfigParser, app_name: str):
		"""
        初始化 AppFlowGraph 实例。

        :param config: 配置文件解析器对象。
        :param app_name: 应用程序名称，用于从配置文件中获取特定应用的设置。
        """
		self.config = config
		self.app_name = app_name
		
		# 获取配置中的路径和表格名称
		config_dir = self.config.get("Paths", "CONFIG_DIR")
		element_file_path = self.config.get(app_name, "PAGE_ELEMENT_FILE_PATH")
		self.excel_file_path = os.path.join(config_dir, element_file_path)
		
		self.adjacency_sheet_name = self.config.get("Tables", "ADJACENCY_LIST")
		self.trust_level_sheet_name = self.config.get("Tables", "TRUST_LEVEL_TABLE")
		
		# 初始化处理器
		self.adjacency_handler = self.create_sheet_handler(self.adjacency_sheet_name)
		self.trust_level_handler = self.create_sheet_handler(self.trust_level_sheet_name)
		
		# 初始化图和元素字典
		self.directed_graph = None
		self.all_page_elements_dict = None
		
		# 构建图和加载元素
		self.build_directed_graph()
		self.load_all_page_elements_into_dict()
	
	def create_sheet_handler(self, sheet_name: str) -> AdjacencyListExcelManager:
		"""
        创建一个 Excel 表格处理器。

        :param sheet_name: 工作表名称。
        :return: 邻接表处理对象。
        """
		return AdjacencyListExcelManager(self.excel_file_path, sheet_name)
	
	def build_directed_graph(self):
		"""
        根据邻接表创建有向图。
        """
		directed_graph = nx.DiGraph()
		
		# 获取所有页面节点和边
		page_nodes = self.adjacency_handler.get_page_names()
		page_transitions = self.adjacency_handler.get_edges().keys()
		
		directed_graph.add_nodes_from(page_nodes)
		directed_graph.add_edges_from(page_transitions)
		
		self.directed_graph = directed_graph
	
	def load_all_page_elements_into_dict(self):
		"""
        加载所有页面及其元素，并存储为字典。
        """
		self.all_page_elements_dict = self.trust_level_handler.read_all_elements_from_excel()
	
	def find_shortest_path_between_pages(self, start_node: str, end_node: str):
		"""
        计算从起始页面到目标页面的最短路径。

        :param start_node: 起始页面名称。
        :param end_node: 目标页面名称。
        :return: 最短路径中包含的所有页面元素。
        """
		path_nodes = nx.dijkstra_path(self.directed_graph, start_node, end_node)
		edge_list = list(zip(path_nodes[:-1], path_nodes[1:]))
		
		return self.retrieve_elements_along_shortest_path(edge_list)
	
	def retrieve_elements_along_shortest_path(self, edge_list):
		"""
        根据边列表获取最短路径所包含的元素列表。

        :param edge_list: 边的列表。
        :return: 元素列表。
        """
		element_list = []
		for edge in edge_list:
			element_list.extend(self.adjacency_handler.get_edges()[edge])
		return element_list
	
	def calculate_page_trust_scores(self, page_content: str):
		"""
		根据页面内容计算每个页面的信任分数。

		该方法会遍历所有预定义的页面元素（self.all_page_elements_dict），根据其配置属性（如 raw 字段），
		检查这些属性是否出现在传入的页面内容中，并结合权重和置信度来累加信任分数。

		:param page_content: 页面的内容字符串，用于匹配元素属性。
		:return: 分数最高的页面名称。
		:raises Exception: 如果最高分数小于等于 1，则抛出异常。
		"""
		
		# 从配置文件中读取权重项（WEIGHTED_ITEM），格式为 JSON 字符串，解析成字典
		WEIGHTED_ITEM: dict = json.loads(self.config.get("Tables", "WEIGHTED_ITEM"))
		
		# 初始化一个空字典，用于保存每个页面的总信任分数
		score_dict = {}
		
		# 遍历所有已知的页面及其对应的元素列表
		for page_name, element_list in self.all_page_elements_dict.items():
			# 如果当前页面尚未在 score_dict 中，初始化得分为 0
			if page_name not in score_dict:
				score_dict[page_name] = 0
			
			# 遍历当前页面的所有元素
			for element in element_list:
				# 获取元素中的 raw 字段（通常是一个 JSON 字符串）
				raw: str = element.get("raw")
				# 将 raw 字符串解析为字典
				raw_dict: dict = json.loads(raw)
				
				# 遍历权重项中的每一个属性（如 text, resource-id 等）和对应权重
				for attr, weight in WEIGHTED_ITEM.items():
					# 获取当前元素中对应属性的值
					value = raw_dict.get(attr)
					
					# 如果值为空或者 NaN（例如 float('nan')），跳过
					if value is None or value != value:
						continue
					
					# 判断该属性值是否出现在页面内容中
					if value in page_content:
						# 计算得分：置信度 * 权重
						# 如果未设置置信度，默认为 0
						score_dict[page_name] += element.get('置信度', 0) * int(weight)
		
		# 打印各页面的得分情况（用于调试或日志记录）
		Logger.info(score_dict)
		
		# 如果所有页面的最高分 <= 1，则抛出异常，表示无法确定正确的页面
		if max(score_dict.values()) <= 1:
			raise Exception("检测页面出错")
		
		# 返回得分最高的页面名称
		return max(score_dict, key=lambda k: score_dict[k])


if __name__ == '__main__':
	from src.com.config_utils import read_config, find_directory
	
	
	def demo():
		"""
        测试 AppFlowGraph 类的功能。
        """
		config_path = os.path.join(find_directory("config"), "app_config.ini")
		config_parser = read_config(config_path)
		app_name = "睿博士"
		
		graph = AppFlowGraph(config_parser, app_name)
		
		# print(graph.adjacency_handler.get_page_names())
		paths = graph.find_shortest_path_between_pages('首页', '扫码配网成功')
		for path in paths:
			print(path)
		# print(graph.all_page_elements_dict)
	
	
	demo()
